﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Amazon.Runtime.Internal;
using DynamicData;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;

namespace Common.Utils.DataBaseHelper
{
    public class MongoDbHelper
    {
        #region enum
        //public enum ConditionType
        //{
        //    And,
        //    Or
        //}

        //public enum CompareType
        //{
        //    Equal,
        //    GreaterThan,
        //    LessThan,
        //    LessThanOrEqual,
        //    GreaterThanOrEqual,
        //    NotEqualTo
        //}

        #endregion

        private static MongoClient s_client;

        public static string GetCollectionNameByDay(string collectioNanme, DateTime date)
        {
            return ($"{collectioNanme}_{date:yyyy-MM-dd}");
        }

        /// <summary>
        /// MongoDB 数据库操作类
        /// </summary>
        public class MongoDataBase<T>
        {
            private IMongoDatabase _database;

            public void InitDatabase(string conString, string dbName)
            {
                s_client ??= new MongoClient(conString);
                _database = s_client.GetDatabase(dbName);
            }

            #region CURD

            public void Insert(string collectionName, T item)
            {
                var collection = _database.GetCollection<T>(collectionName);
                if (collection != null)
                {
                    collection.InsertOne(item);
                }
            }

            public Task InsertAsync(string collectionName, T item)
            {
                var collection = _database.GetCollection<T>(collectionName);
                if (collection != null)
                {
                    return collection.InsertOneAsync(item);
                }

                return Task.CompletedTask;
            }

            public void BulkWrite(string collectionName, IEnumerable<T> list)
            {
                var collection = _database.GetCollection<T>(collectionName);
                if (collection != null)
                {
                    var wMList = new List<InsertOneModel<T>>();
                    wMList.AddRange(list.Select(c => new InsertOneModel<T>(c)));
                    collection.BulkWrite(wMList);
                }
            }

            public Task BulkWriteAsync(string collectionName, IEnumerable<T> list)
            {
                var collection = _database.GetCollection<T>(collectionName);
                if (collection != null)
                {
                    var wMList = new List<InsertOneModel<T>>();
                    wMList.AddRange(list.Select(c => new InsertOneModel<T>(c)));
                    return collection.BulkWriteAsync(wMList);
                }

                return Task.CompletedTask;
            }

            public IEnumerable<T> GetList(string collectionName)
            {
                var collection = _database.GetCollection<T>(collectionName);
                return collection.Find(new BsonDocument()).ToList();
            }

            public Task<IEnumerable<T>> GetListAsync(string collectionName)
            {
                return Task.Run(() =>
                {
                    var collection = _database.GetCollection<T>(collectionName);
                    var result = collection.FindAsync(new BsonDocument()).Result.ToEnumerable();
                    return result;
                });
            }

            public IEnumerable<T> GetFilterList(string collectionName, Expression<Func<T, bool>> predicate,  string? sortField = null)
            {
                var collection = _database.GetCollection<T>(collectionName);
                var filter = Builders<T>.Filter.Where(predicate);
                if (string.IsNullOrEmpty((sortField)))
                {
                    return collection.Find(filter).ToList();
                }
                else
                {
                    return collection.Find(filter).Sort(sortField).ToList();
                }
            }

            public async Task<IEnumerable<T>> GetFilterListAsync(string collectionName, Expression<Func<T, bool>> predicate)
            {
                var collection = _database.GetCollection<T>(collectionName);
                var filter = Builders<T>.Filter.Where(predicate);
                return (await collection.FindAsync(filter)).ToEnumerable();
            }

            //public IEnumerable<T> GetFilterList(List<Tuple<string, object, CompareType?>> conditions, ConditionType type = ConditionType.And)
            //{
            //    var filterBuilder = Builders<T>.Filter;
            //    var definitions = new FilterDefinition<T>[] { };
            //    foreach (var condition in conditions)
            //    {
            //        switch (condition.Item3)
            //        {
            //            case CompareType.NotEqualTo:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Ne(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.LessThan:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Lt(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.GreaterThan:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Gt(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.GreaterThanOrEqual:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Gte(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.LessThanOrEqual:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Lte(condition.Item1, condition.Item2) });
            //                break;
            //            default:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Eq(condition.Item1, condition.Item2) });
            //                break;
            //        }
            //    }
            //    if (type == ConditionType.And)
            //    {
            //        return collection.Find(filterBuilder.And(definitions)).ToList();
            //    }
            //    else
            //    {
            //        return collection.Find(filterBuilder.Or(definitions)).ToList();
            //    }
            //}

            //public Task<IEnumerable<T>> GetFilterListAsync(List<Tuple<string, object, CompareType?>> conditions, ConditionType type = ConditionType.And)
            //{
            //    var filterBuilder = Builders<T>.Filter;
            //    var definitions = new FilterDefinition<T>[] { };
            //    foreach (var condition in conditions)
            //    {
            //        switch (condition.Item3)
            //        {
            //            case CompareType.NotEqualTo:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Ne(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.LessThan:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Lt(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.GreaterThan:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Gt(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.GreaterThanOrEqual:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Gte(condition.Item1, condition.Item2) });
            //                break;
            //            case CompareType.LessThanOrEqual:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Lte(condition.Item1, condition.Item2) });
            //                break;
            //            default:
            //                definitions.Add(new FilterDefinition<T>[] { Builders<T>.Filter.Eq(condition.Item1, condition.Item2) });
            //                break;
            //        }
            //    }

            //    return Task.Run(() =>
            //    {
            //        if (type == ConditionType.And)
            //        {
            //            return collection.FindAsync(filterBuilder.And(definitions)).Result.ToEnumerable();
            //        }
            //        else
            //        {
            //            return collection.FindAsync(filterBuilder.Or(definitions)).Result.ToEnumerable();
            //        }
            //    });
            //}

            public void Delete(string collectionName, Expression<Func<T, bool>> predicate)
            {
                var collection = _database.GetCollection<T>(collectionName);
                var filter = Builders<T>.Filter.Where(predicate);
                collection.DeleteMany(filter);
            }

            public void Update(string collectionName, Expression<Func<T, bool>> predicate, Expression<Func<T, object>> field, object value)
            {
                var collection = _database.GetCollection<T>(collectionName);
                var filter = Builders<T>.Filter.Where(predicate);
                var update = Builders<T>.Update.Set(field, value);
                collection.UpdateMany(filter, update);
            }

            public void DropCollection(string collectionName)
            {
                _database.DropCollection(collectionName);

            }

            public IEnumerable<string> ListCollectionNames()
            {
                var list = _database.ListCollectionNames();
                if (list.MoveNext())
                {
                    return list.Current;
                }
                else
                {
                    return null;
                }
            }

            #endregion
        }
    }

    public class IDEntity
    {
        public ObjectId _id { get; set; } = ObjectId.GenerateNewId();
    }
}
